/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import path from 'path';

import { cloneDeep, difference } from '@baize/wdk';
import {FileSet, hvigorCore, Json5Reader, TaskInputValue} from '@ohos/hvigor';
import * as fs from 'fs-extra';
import fse from 'fs-extra';

import { ModulePathInfoIml } from '../common/iml/module-path-info-iml.js';
import { AbilityConst, ArkTSModeConst, ArkTSVersionConst, CommonConst, LanguageConst } from '../const/common-const.js';
import { VirtualMachine } from '../enum/virtual-machine-enum.js';
import { ModuleJson } from '../options/configure/module-json-options.js';
import { InjectUtil } from '../utils/inject-util.js';
import { getAppInfoByProject } from '../utils/integrated-hsp-utils.js';
import { TaskNames } from './common/task-names.js';
import { ModuleTargetData } from './data/hap-task-target-data.js';
import { MergeProfile } from './merge-profile.js';
import { TargetTaskService } from './service/target-task-service.js';
import { OhosHapTask } from './task/ohos-hap-task.js';

import DependencyObj = ModuleJson.DependencyObj;
import Task = TaskNames.Task;

import { getJson5Obj } from '../utils/json-util.js';
import { generateOhmurlForSrcEntry, generateOhmUrlV2ForSrcEntry } from '../utils/ohmurl-utils.js';

import AbilityObj = ModuleJson.AbilityObj;
import ModuleOptObj = ModuleJson.ModuleOptObj;
import ExtensionAbilityObj = ModuleJson.ExtensionAbilityObj;

import { OhosLogger } from '../utils/log/ohos-logger.js';

const INPUT_KEY = {
  arkEnable: 'arkEnable',
  compileMode: 'compileMode',
  deviceTypes: 'deviceTypes',
  dependency: 'dependency',
  harExcludeHSPDependencies:'harExcludeHSPDependencies',
};

const _log: OhosLogger = OhosLogger.getLogger('hvigor-process-profile');

/**
 * 处理module.json
 * 1. 写入ark字段
 * 2. 写入compileMode字段
 * 3. 替换target相关逻辑
 *  3.1 读取target的deviceType, 替换到module.json中
 *
 * @since 2022/1/10
 */
export class ProcessProfile extends OhosHapTask {
  private readonly _arkEnable: boolean;
  private readonly _deviceTypes: string[];
  private readonly _intermediatesMergeProfile: string;
  private readonly _processedModuleJson: string;
  private _moduleTargetData: ModuleTargetData;
  private _pathInfo: ModulePathInfoIml;
  private _dependencies: DependencyObj[] = [];
  private harExtensionAbilities: ExtensionAbilityObj[] = [];
  constructor(taskService: TargetTaskService) {
    super(taskService, Task.PROCESS_PROFILE);
    this._arkEnable = this.service.isArkModule();
    this._moduleTargetData = this.targetService.getTargetData();
    this._deviceTypes = this._moduleTargetData.getTargetDeviceType();
    this._pathInfo = this._moduleTargetData.getPathInfo();
    this._intermediatesMergeProfile = this._pathInfo.getIntermediatesMergeProfile();
    this._processedModuleJson = this._pathInfo.getIntermediatesProcessProfile();
    this.mergeHspLibs().forEach((hspInfo) => this._dependencies.push(hspInfo));
  }

  initTaskDepends() {
    this.declareDepends(MergeProfile.name);
  }

  private shouldCollectDependenciesAbilities() {
    // localTest不需要收集
    if (InjectUtil.isLocalTest()) {
      return false;
    }
    // hap | hsp | har in ohosTest | har in preview
    return this.moduleModel.isHapModule() || this.moduleModel.isHspModule() || InjectUtil.isOhosTest() || InjectUtil.isPreviewProcess();
  }

  async beforeAlwaysAction(): Promise<void> {
    await super.beforeAlwaysAction();
    if (this.shouldCollectDependenciesAbilities()) {
      this.harExtensionAbilities = this.collectHarAbilities(AbilityConst.EXTENSION_ABILITY) as ExtensionAbilityObj[];
    }
    this.validateExtensionAbilities();
  }

  private validateExtensionAbilities() {
    const moduleJson: ModuleJson.ModuleOptObj = getJson5Obj(this._intermediatesMergeProfile);
    const extensionAbilities = moduleJson.module.extensionAbilities || [];
    extensionAbilities.push(...this.harExtensionAbilities);
    if (!extensionAbilities.length) {
      return;
    }
    const duplicatedNames = this.findDuplicatedAbilityNames(extensionAbilities);
    if (!duplicatedNames.length) {
      return;
    }
    _log.warn(
      `Duplicate 'Extension-Abilities' object names detected: ${JSON.stringify(duplicatedNames)}. 
    Make sure the names are unique across the current module and the modules on which it depends.`,
    );
  }

  protected doTaskAction(): void {
    const mergedModuleOpt: ModuleJson.ModuleOptObj = getJson5Obj(this._intermediatesMergeProfile);
    const compatibleApi = this.projectModel.getCompatibleApiMetaByProduct(this.targetData.getProduct().name).version;

    // 增加ark配置
    mergedModuleOpt.module.virtualMachine = this._arkEnable
      ? `${VirtualMachine.ARK}${this.sdkInfo.getArkVersion(compatibleApi, this.compatibleSdkVersionStage)}`
      : VirtualMachine.DEFAULT;

    // 增加模块化编译配置
    mergedModuleOpt.module.compileMode = this.targetCompileMode;

    // 替换target中配置的deviceType
    mergedModuleOpt.module.deviceTypes = this._deviceTypes;

    // 如果不是har，添加module.json中的dependencies字段
    this.addModuleDependencies(mergedModuleOpt);



    if (mergedModuleOpt) {
      // 处理module、ability信息
      this.processModuleAndAbility(mergedModuleOpt);

      // 处理extensionAbility
      this.processExtensionAbility(mergedModuleOpt);

      // 处理ohosTest的testRunner配置
      this.processTestRunner(mergedModuleOpt);
    }

    // 给ark编译新生成一个文件 从merge_profile里取出 不作处理
    const moduleForArkJsonPath = this.pathInfo.getIntermediatesArkModuleJsonPath();
    fs.ensureFileSync(moduleForArkJsonPath);
    fs.writeFileSync(moduleForArkJsonPath, JSON.stringify(mergedModuleOpt));

    // 合并har包含的ability
    this.processDependencyHarAbility(mergedModuleOpt);

    fs.outputJSONSync(this._processedModuleJson, mergedModuleOpt, { spaces: '\t' });
  }

  declareInputs(): Map<string, TaskInputValue> {
    const inputs = new Map<string, TaskInputValue>();
    inputs.set(INPUT_KEY.arkEnable, this._arkEnable);
    inputs.set(INPUT_KEY.compileMode, this.targetCompileMode);
    inputs.set(INPUT_KEY.deviceTypes, this._deviceTypes);
    inputs.set(INPUT_KEY.dependency, JSON.stringify(this._dependencies));
    inputs.set(INPUT_KEY.harExcludeHSPDependencies, hvigorCore.getParameter().getProperty(CommonConst.HAR_EXCLUDE_HSP_DEPENDENCIES));
    inputs.set('compatibleSdkVersionStage', this.compatibleSdkVersionStage || '');
    return inputs;
  }

  declareInputFiles(): FileSet {
    return new FileSet().addEntry(this._intermediatesMergeProfile);
  }

  declareOutputFiles(): FileSet {
    return new FileSet().addEntry(this._processedModuleJson);
  }

  private mergeHspLibs(): DependencyObj[] {
    // 三方依赖的module.json
    const hspList: string[] = this.service.getHspDependencyPaths();
    const localModuleList: string[] = this.service.getHspModuleDependencyPathsWithOutDynamic();
    const { versionCode } = getAppInfoByProject(this.projectModel);

    return [
      ...difference(hspList, localModuleList).map((hspPath) => {
        const moduleJson = path.resolve(hspPath, 'src', 'main', 'module.json');
        const moduleJson5 = path.resolve(hspPath, 'src', 'main', 'module.json5');
        return fse.existsSync(moduleJson) ? moduleJson : moduleJson5;
      }),
      ...localModuleList.map((hspPath) => {
        return path.resolve(hspPath, 'src', 'main', 'module.json5');
      }),
    ].map((hspPath) => {
      let hspModuleJson: ModuleJson.ModuleOptObj;
      if (hspPath.endsWith('module.json')) {
        hspModuleJson = fse.readJsonSync(hspPath);
      } else {
        hspModuleJson = Json5Reader.getJson5Obj(hspPath);
      }
      return {
        moduleName: hspModuleJson.module.name,
        versionCode: hspModuleJson?.app?.bundleName ? hspModuleJson?.app?.versionCode ?? versionCode : versionCode,
      };
    });
  }

  private collectHarAbilities(abilityTypeName: string) {
    const result: AbilityObj[] | ExtensionAbilityObj[] = [];
    this.service.getHarDependencies().forEach((dependency) => {
      const dependencyModuleJsonObj = dependency.getModuleJsonObj();
      if (!dependencyModuleJsonObj) {
        return;
      }

      const depModuleAbilities = dependencyModuleJsonObj.module?.[abilityTypeName] as ModuleJson.AbilityObj[] | undefined;

      if (!depModuleAbilities?.length) {
        return;
      }

      depModuleAbilities.forEach((abilityObj: AbilityObj) => {
        const dependencyAbilities = cloneDeep(abilityObj);
        if (!dependencyAbilities.srcEntry) {
          return;
        }

        Object.assign(dependencyAbilities, {
          srcEntry: generateOhmurlForSrcEntry(this.targetService, this.moduleName, dependency, dependencyAbilities.srcEntry),
          moduleName: dependency.getDependencyName(),
          // 最终打包的产物中的module.json5中，ability添加codeLanguage字段
          arkTSMode: dependencyAbilities.arkTSMode === ArkTSVersionConst.ARKTS1_2 ? ArkTSModeConst.STATIC :ArkTSModeConst.DYNAMIC
        });

        result.push(dependencyAbilities);
      });
    });
    return result;
  }

  private addHarAbilitiesToModuleJson(moduleOptObj: ModuleOptObj, abilityTypeName: string, abilities: AbilityObj[]) {
    if (!abilities.length) {
      return;
    }
    moduleOptObj.module[abilityTypeName] ||= [];
    (moduleOptObj.module[abilityTypeName] as AbilityObj[]).push(...abilities);
  }

  /**
   * 构建hap、hsp时支持将直接、间接依赖har的module.json的abilities以及extensionAbilities合并到最终的hap、hsp产物module.json中，合并abilities时如果有name相同的则进行报错
   * @private
   */
  private processDependencyHarAbility(moduleOptObj: ModuleOptObj) {
    if (!moduleOptObj || !this.shouldCollectDependenciesAbilities) {
      return;
    }

    const harUIAbilities = this.collectHarAbilities(AbilityConst.UI_ABILITY);
    this.addHarAbilitiesToModuleJson(moduleOptObj, AbilityConst.UI_ABILITY, harUIAbilities);
    this.addHarAbilitiesToModuleJson(moduleOptObj, AbilityConst.EXTENSION_ABILITY, this.harExtensionAbilities);

    // 检查合并后的abilities是否包含重复name的ability
    this.checkDuplicateAbility(moduleOptObj.module[AbilityConst.UI_ABILITY] as ModuleJson.AbilityObj[] | undefined);
  }

  private findDuplicatedAbilityNames(moduleAndDepAbilities: AbilityObj[] | ExtensionAbilityObj[]) {
    if (!moduleAndDepAbilities?.length) {
      return [];
    }
    const abilityObjName2ModuleNameMap = new Map<string, string[]>();
    moduleAndDepAbilities.forEach((abilityObj: AbilityObj) => {
      const moduleName = abilityObj.moduleName ?? this.moduleName;
      if (abilityObjName2ModuleNameMap.has(abilityObj.name)) {
        abilityObjName2ModuleNameMap.get(abilityObj.name)?.push(moduleName);
      } else {
        abilityObjName2ModuleNameMap.set(abilityObj.name, [moduleName]);
      }

      if (abilityObj.moduleName) {
        delete abilityObj.moduleName;
      }
    });
    return Array.from(abilityObjName2ModuleNameMap.entries()).filter(([key, mapValue]) => mapValue.length > 1);
  }

  /**
   * 检查依赖har中abilities数组的ability对象是否存在重复name
   * @private
   * @param moduleAndDepAbilities
   */
  private checkDuplicateAbility(moduleAndDepAbilities: AbilityObj[] | undefined): void {
    if (!moduleAndDepAbilities?.length) {
      return;
    }
    const errorFilterArr = this.findDuplicatedAbilityNames(moduleAndDepAbilities);

    if (errorFilterArr.length !== 0) {
      _log.printErrorExit('DUPLICATE_MODULE_ABLITIES_OBJECT_NAMES_DETECTED', undefined, [[JSON.stringify([...errorFilterArr])]]);
    }
  }

  /**
   * 添加模块依赖项
   * 对于模块HAR并且HAR_EXCLUDE_HSP_DEPENDENCIES参数为true，则不为模块添加的dependencies
   * @param mergedModuleOpt 合并后的模块选项对象
   */
  private addModuleDependencies(mergedModuleOpt: ModuleJson.ModuleOptObj) {
    // 此处判断properties是否为true用于处理命令行传入类型不为布尔值场景
    if (this.moduleModel.isHarModule() && hvigorCore.getParameter().getProperty(CommonConst.HAR_EXCLUDE_HSP_DEPENDENCIES) === true) {
      return;
    }
    mergedModuleOpt.module.dependencies = this._dependencies;
  }

  /**
   * 构建态时处理module.json5中的数据
   * @param moduleOptObj
   * @private
   */
  private processModuleAndAbility(moduleOptObj: ModuleOptObj): void {
    if (moduleOptObj.module) {
      let moduleCodeLanguage = ArkTSModeConst.DYNAMIC;

      if (this.targetService.verifyModuleVersionIsArkTS12()) {
        // 如果当前模块是ArkTS1.2，codeLanguage为1.2
        moduleCodeLanguage = ArkTSModeConst.STATIC;

        // abilityStage的srcEntry转换ohmurl
        const abilityStageSrcEntry = moduleOptObj.module.srcEntry;
        abilityStageSrcEntry && (moduleOptObj.module.srcEntry = generateOhmUrlV2ForSrcEntry(
          this.targetService.getPkgContextInfo(this.packageJsonObj.name),
          abilityStageSrcEntry!
        ));
      }

      // module.json5中，module属性下增加codeLanuage标识
      moduleOptObj.module.moduleArkTSMode = moduleCodeLanguage;
    }

    moduleOptObj.module?.abilities?.forEach(ability => {
      if (this.targetService.verifyModuleVersionIsArkTS12() && !ability.arkTSMode) {

        // ability中增加arkTSMode
        ability.arkTSMode = ability.arkTSMode ?? ArkTSModeConst.STATIC;

        // srcEntry转换为ArkTS1.2的ohmurl形式
        ability.srcEntry = generateOhmUrlV2ForSrcEntry(
          this.targetService.getPkgContextInfo(this.packageJsonObj.name),
          ability.srcEntry!
        );
      }
    });
  }

  /**
   * 编译态处理 extensionAbility信息
   * @param moduleOptObj
   * @private
   */
  private processExtensionAbility(moduleOptObj: ModuleOptObj) {
    moduleOptObj?.module?.extensionAbilities?.forEach(ability => {
      if (this.targetService.getSelfArkTSVersion() === ArkTSVersionConst.ARKTS1_2) {
        ability.arkTSMode = ArkTSModeConst.STATIC;
        ability.srcEntry = generateOhmUrlV2ForSrcEntry(this.targetService.getPkgContextInfo(this.packageJsonObj.name), ability.srcEntry!);
      }
      ability.arkTSMode = ability.arkTSMode ?? ArkTSModeConst.DYNAMIC;
    });
  }

  /**
   * 处理testRunner转换
   * @param moduleOptObj
   * @private
   */
  private processTestRunner(moduleOptObj: ModuleOptObj) {
    const testRunnerPath = moduleOptObj?.module?.testRunner?.srcPath;
    if (!testRunnerPath || !this.targetService.verifyModuleVersionIsArkTS12()) {
      return;
    }

    moduleOptObj!.module!.testRunner!.srcPath =
      generateOhmUrlV2ForSrcEntry(this.targetService.getPkgContextInfo(this.packageJsonObj.name), testRunnerPath, 'src/ohosTest',true);
    moduleOptObj!.module!.testRunner!.arkTSMode = ArkTSModeConst.STATIC;
  }
}
